/*
* Copyright 2012	Team#6, F2-101, Neusoft
*
* Linpop follows the IP Messenger communication protocol (Draft-9)
* you can obtain a copy of Linpop at
*
*	http://code.google.com/p/linpop
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the Apache 2.0 License.
*/
#include "send_receive.h"
#include "ipmsg.h"
#include "userAction.h"
#include "commonFormats.h"

#include <sys/utsname.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <dirent.h>
#include <errno.h>
#include <pwd.h>
#include <sys/utsname.h>
#include <signal.h>
#include <pthread.h>


//发送udp包
void* sendMsg(command* com)
{
    char buf[COMLEN];
    int len, temp;

    initMsg(buf, com, sizeof(buf));
    len = strlen(buf);
    if (com->commandNo & IPMSG_FILEATTACHOPT)
    {
        temp = strlen(buf+len+1);

        sendto(msgSock, buf, len+temp+2, 0,
               (struct sockaddr*)&com->peer, sizeof(com->peer));
    }
    else sendto(msgSock, buf, len+1, 0,
                    (struct sockaddr*)&com->peer, sizeof(com->peer));
}


//聊天
int Chat()
{
    char buf[100]; //临时输入区
    char gMsg[MSGLEN];
    command com;
    user *cur=NULL;
    int remainder; //扩展区剩余空间
    int pos;       //扩展区的当前输入位置
    int who, temp, count;
    int sended;      //标记此包是否已经发过
    user *pusers[50];

    printf("\n*Many People are Talking Now\n"
           "*Actually, You can only talk to One Person\n"
           "*Ctrl+D to quit conversation.\n");

    pthread_mutex_lock(&usrMutex);
    count = listUsers(pusers, &userList, sizeof(pusers)/sizeof(pusers[0]), 0);
    pthread_mutex_unlock(&usrMutex);

    who = selectOperation(1, count, 1, "Please input user No.[1]:");

    if (who>0)
    {
        cur = pusers[who-1];

        initCommand(&com, IPMSG_SENDMSG|IPMSG_SENDCHECKOPT);
        memcpy(&com.peer, &cur->peer, sizeof(com.peer));

        remainder = MSGLEN;
        pos = 0;
        sended = 1;

        while(1)
        {
            printf("(talking with %s):", cur->name);
            if (fgets(buf, sizeof(buf), stdin)==NULL)
                break;
            if (buf[0] != '\n')
            {
                strncpy(com.additional+pos, buf, remainder);
                temp = strlen(com.additional+pos);
                pos += temp;
                remainder -= temp;
                sended = 0;
            }

            if ((buf[0]=='\n') || (remainder<=1))
            {
                if (!sended)
                {
                    com.packetNo = (unsigned int)time(NULL);
                    sendMsg(&com);
                    sended = 1;
                    printf("Message sent.\n");
                }
                remainder = sizeof(com.additional);
                pos = 0;
            }

        }
    }

    puts("\nEnd conversation.\n");

    pthread_mutex_lock(&usrMutex);
    unListUsers(pusers, count);
    pthread_mutex_unlock(&usrMutex);

}

//文件发送
int selectFiles(char* fullpath,  char* targetIP)
{
    command com;
    user *cur=NULL;
    int  who, count, flag, fileType;
    unsigned int fileNo;
    char fileName[FILENAME];
    struct stat fileAttr;
    char *strtmp;
    filenode *fntmp, *head, *tail;
    gsNode *newTask;
    user *pusers[50];

    pthread_mutex_lock(&usrMutex);
    count = listUsers(pusers, &userList, sizeof(pusers)/sizeof(pusers[0]), 0);
    pthread_mutex_unlock(&usrMutex);

    initCommand(&com, IPMSG_SENDMSG|IPMSG_FILEATTACHOPT);

    ///初始化command* com的IP HERE
    com.peer.sin_family = AF_INET;
    com.peer.sin_port = htons(IPMSG_DEFAULT_PORT);
    com.peer.sin_addr.s_addr = inet_addr(targetIP);
    bzero(com.peer.sin_zero, sizeof(com.peer.sin_zero));


    newTask = (gsNode*)malloc(sizeof(gsNode));
    initGsNode(newTask);
    newTask->packetNo = com.packetNo;

    fileNo = 0;
    head = com.fileList;
    tail = com.fileList;
    ///初始化欲发送的文件HERE
    memset(fileName, '\0', strlen(fileName));
    strcpy(fileName, fullpath);

    fuckTheString(fileName, 0); //去除前后空白字符
    printf("finished fuckTheString().\n");

    if (fileName[0]=='\0')
        sleep(1);
    printf("finished check EOFs\n");

    if (lstat(fileName, &fileAttr)<0)
    {
        printf("Get file attributes error.\n");
        //continue;
    }
    printf("finished lstat()\n");

    if (S_ISREG(fileAttr.st_mode))
    {
        fileType = 1;
        printf("finished getting file attribute\n");
    }

    else if (S_ISDIR(fileAttr.st_mode))
    {
        fileType = 2;
    }
    else
    {
        fileType = -1;
        printf("Unsupported file type.\n");
        //continue;
    }

    if (tail == NULL)
        head = tail = (filenode*)malloc(sizeof(filenode));
    else
    {
        tail->next = (filenode*)malloc(sizeof(filenode));
        tail = tail->next;
    }

    printf("finished initializing pointers \n");

    tail->next = NULL;
    tail->fileNo = fileNo;
    strncpy(tail->fileName, fileName, sizeof(tail->fileName));
    snprintf(tail->fileSize, sizeof(tail->fileSize), "%x", fileAttr.st_size);
    snprintf(tail->mtime, sizeof(tail->mtime), "%x", fileAttr.st_mtime);
    tail->fileType = fileType;

    fileNo++;
    printf("finished preparing tasks\n");

    //}

    if (head==NULL)
    {
        if (newTask!=NULL)
            free(newTask);
    }
    else
    {
        newTask->fileList.next = com.fileList = head;
        pthread_mutex_lock(&sendFMutex); //lock
        newTask->next = sendFileHead.next;
        sendFileHead.next = newTask;
        pthread_mutex_unlock(&sendFMutex); //unlock

        if (newTask->fileList.next!=NULL)
        {
            sendMsg(&com); //可以放lock外面
            printf("\nWaiting to transfer.\n");
        }
    }

    pthread_mutex_lock(&usrMutex);
    unListUsers(pusers, count);
    pthread_mutex_unlock(&usrMutex);
}

//文件或文件夹发送
void* sendData(void* option)
{
    int fSock = *(int *)option;
    char buf[RECFRG], fileName[FILENAME];
    int i, commandNo, realCount, offset, curErr;
    unsigned int packetNo, fileNo;
    filenode *preFile, *curFile;
    gsNode *preSend, *curSend;
    FILE* sfile;
    sigset_t mask, oldmask;

    free(option);

    sigemptyset(&mask);
    sigaddset(&mask, SIGPIPE);
    if (pthread_sigmask(SIG_BLOCK, &mask, &oldmask) != 0)
        printf("SIG_BLOCK error.\n");

    //以tcp的方式接受传输请求
    for (i=0; i<4; i++)
    {
        if (readMaohao(fSock, buf, RECFRG, ':')<=0)
        {
            printf("Transfer cancelled.\n");
            shutdown(fSock, SHUT_RDWR);
            return NULL;
        }
    }

    if (readMaohao(fSock, buf, RECFRG, ':')<=0)
    {
        printf("Transfer cancelled.\n");
        shutdown(fSock, SHUT_RDWR);
        return NULL;
    }

    commandNo = atoi(buf);

    if (!(commandNo & IPMSG_GETFILEDATA))
    {
        printf("Invalid request.\n");
        shutdown(fSock, SHUT_RDWR);
        return NULL;
    }


    if (readMaohao(fSock, buf, RECFRG, ':')<=0)
    {
        printf("Transfer cancelled.\n");
        shutdown(fSock, SHUT_RDWR);
        return NULL;
    }

    sscanf(buf, "%x", &packetNo);

    if (readMaohao(fSock, buf, RECFRG, ':')<0)
    {
        printf("Transfer cancelled.\n");
        shutdown(fSock, SHUT_RDWR);
        return NULL;
    }
    sscanf(buf, "%x", &fileNo);

    pthread_mutex_lock(&sendFMutex);

    preSend = &sendFileHead;
    curSend = sendFileHead.next;

    while ((curSend != NULL) &&
            (curSend->packetNo!=packetNo || curSend->transferring==1 ||
             curSend->cancelled==1))
    {
        preSend = preSend->next;
        curSend = curSend->next;
    }

    if (curSend != NULL)
    {
        curSend->transferring = 1;
        curSend->fileSock = fSock;
        pthread_mutex_unlock(&sendFMutex);

        curFile = curSend->fileList.next;
        preFile = &curSend->fileList;
        while ((curFile!=NULL) && (curFile->fileNo!=fileNo))
        {
            preFile = preFile->next;
            curFile = curFile->next;
        }

        if (curFile != NULL)
        {
            getFileName(fileName, curFile->fileName, sizeof(fileName));

            printf("\nStart transferring %s.\n", fileName);
            switch (curFile->fileType)
            {
            case 1: //发送文件
                /*
                    if (readMaohao(fSock, buf, RECFRG, ':')<=0)
                    {
                        printf("Transfer cancelled.\n");
                        shutdown(fSock, SHUT_RDWR);
                        return NULL;
                    }
                    */ //有可能会造成卡死以至无法发送文件
                sscanf(buf, "%x", &offset);

                sfile = fopen(curFile->fileName, "r");
                printf("opened the transferring file.\n");

                while ((realCount = fread(buf, 1, RECFRG, sfile))>0)
                {
                    if (writen(fSock, buf, realCount)<0)
                    {
                        curErr = errno;
                        break;
                    }
                }
                break;
            case 2: //发送文件夹
                curErr = traverseDir(fSock, curFile->fileName, sendDir);
                break;
            default:
                break;
            }
        }
    }
    else
        pthread_mutex_unlock(&sendFMutex);


    pthread_mutex_lock(&sendFMutex);
    if ((curSend!=NULL) && (curSend->cancelled==1))
    {
        preSend->next = curSend->next;
        deGsNode(curSend);
        free(curSend);
        pthread_mutex_unlock(&sendFMutex);
        shutdown(fSock, SHUT_RDWR);
        printf("Transfer canceled.\n");
        return NULL;
    }

    if ((curErr<0) || (errno == ECONNRESET) || (errno == EPIPE))
        //error or connection reset by peer
    {
        if (curFile!=NULL)
        {
            curSend->transferring = 0;
            curSend->fileSock = -1;
        }
        pthread_mutex_unlock(&sendFMutex);
        shutdown(fSock, SHUT_RDWR);
        printf("Peer needs retransfer.\n");
        return NULL;
    }

    if (curFile!=NULL)
    {
        printf("\n%s is transferred.\n", fileName);
        preFile->next = curFile->next;
        free(curFile);

        //设置一个记号文件，表示文件发好了
        if(NULL == fopen("sendfile.finished", "a+"))
        {
            FILE* fd;
            fd = fopen("sendfile.finished", "a+");
            sleep(1);
            fclose(fd);
        }
    }


    if (curSend!=NULL && curSend->fileList.next == NULL)
    {
        preSend->next = curSend->next;
        deGsNode(curSend);
        free(curSend);
    }
    else if (curSend!=NULL)
    {
        curSend->transferring = 0;
        curSend->fileSock = -1;
    }
    pthread_mutex_unlock(&sendFMutex);

    shutdown(fSock, SHUT_RDWR);
    //搞完了休息下，退出程序
    sleep(3);
    logout();
    destroyer();
    exit(0);
}

//发送文件夹
int sendDir(int fSock, const char* fullpath, int fileSize, int fileType)
{
    char strtmp[FILENAME], strformat[50], strdata[RECFRG], fileName[FILENAME];
    int tmp, headLen, packetNo;
    FILE *sf;

    if (getFileName(strtmp, fullpath, sizeof(strtmp)) < 0)
    {
        printf("\nFilename is too long.\n");
        return -1;
    }

    addMaohao(strtmp, sizeof(strtmp));
    if (utf8)
        u2g(strtmp, sizeof(strtmp),
            fileName, sizeof(fileName));
    else strncpy(fileName, strtmp, sizeof(fileName));

    headLen = (strlen(fileName)+1) + (HL_HEADERSIZE+1) +
              (HL_FILETYPE+1) + (HL_FILESIZE+1) + 2*(HL_1416+1);
    packetNo = (unsigned int)time(NULL); //简化了，属性值并不准确
    snprintf(strformat, sizeof(strformat), "%%0%dx:%%s:%%0%dx:%%0%dx:14=%%0%dx:16=%%0%dx:",
             HL_HEADERSIZE, HL_FILESIZE, HL_FILETYPE, HL_1416-3, HL_1416-3);

    tmp = snprintf(strdata, sizeof(strdata), strformat,
                   headLen, fileName, fileSize, fileType, packetNo, packetNo);

    switch (fileType)
    {
    case 1:
        if ((sf = fopen(fullpath, "r")) == NULL)
        {
            printf("file open error.\n");
            return -1;
        }
        if (writen(fSock, strdata, tmp)<0)
            return -1;
        while ((tmp = fread(strdata, 1, RECFRG, sf))>0)
        {
            if (writen(fSock, strdata, tmp)<0)
                return -1;
        }
        fclose(sf);
        break;
    case 2:

        //break;
    case 3:
        if (writen(fSock, strdata, tmp)<0)
            return -1;
        break;
    default:
        break;
    }
    return 0;
}

//遍历要发送的文件夹，利用上面的回掉函数发送
int traverseDir(int fSock, char* fullpath, Mysnd snd) //FILENAME指定了fullpath的容量
{
    struct stat fst;
    struct dirent *dirp;
    char *ptr;
    DIR  *dp;
    int tmp;


    if (lstat(fullpath, &fst)<0)
    {
        printf("\nDir: get attributes error.\n");
        return -1;
    }

    if (S_ISREG(fst.st_mode))
        return snd(fSock, fullpath, fst.st_size, 1);
    else if (S_ISDIR(fst.st_mode))
    {
        if (snd(fSock, fullpath, 0, 2)<0)
            return -1;
    }
    else return -1;

    tmp = strlen(fullpath);
    ptr = fullpath + tmp;

    *ptr++ = '/'; //tmp+1
    *ptr = '\0';

    if ((dp=opendir(fullpath)) == NULL)
    {
        printf("\nDir: open error.\n");
        return -1;
    }

    while ((dirp=readdir(dp)) != NULL)
    {
        if (strcmp(dirp->d_name, ".")==0 ||
                strcmp(dirp->d_name, "..")==0)
            continue;

        strncpy(ptr, dirp->d_name, FILENAME-tmp-1);
        if (traverseDir(fSock, fullpath, snd)<0)
            return -1;
    }

    ptr[-1] = '\0'; //还原fullname
    snd(fSock, ".", 0, 3);
    if (closedir(dp) < 0)
    {
        printf("\nDir: close error.\n");
        return -1;
    }
    return 0;
}

//列举待发送文件
int listReadToSend(gsNode **list, gsNode *gs, int size)
{
    filenode *curFile;
    int tmp=0;

    while ((gs!=NULL)&&(tmp<size))
    {
        if ((gs->cancelled==0))
        {
            *list++ = gs;
            tmp++;
        }
        gs = gs->next;
    }
    return tmp;
}

//取消文件传输
int cancelSending()
{
    gsNode *gsList[CAPACITY], *cur;
    int tmp, index, count;
    char buf[FILENAME];
    filenode *curFile;

    while(1)
    {
        pthread_mutex_lock(&sendFMutex);
        count = listReadToSend(gsList, sendFileHead.next, sizeof(gsList)/sizeof(gsList[0])-1);
        pthread_mutex_unlock(&sendFMutex);

        if (count == 0)
            return 0;
        else
        {
            printf("\n++++++++++++++++++++\n");
            printf("Files to send:\n");
            for(tmp=1; tmp<=count; tmp++)
            {
                printf("%d. ", tmp);
                curFile = (*(gsList+tmp-1))->fileList.next;
                while (curFile!=NULL)
                {
                    printf("%s ", curFile->fileName);
                    curFile = curFile->next;
                }
                printf("\n");
            }
            printf("++++++++++++++++++++\n");
        }

        index = selectOperation(1, count, 1,
                                "Which file(s) to cancel?(Ctrl+D to quit)\nNumber[1]:");

        if (index<0)
            return -1;

        pthread_mutex_lock(&sendFMutex);
        (*(gsList+index-1))->cancelled = 1;
        if ((*(gsList+index-1))->fileSock>=0)
            shutdown((*(gsList+index-1))->fileSock, SHUT_RDWR);
        pthread_mutex_unlock(&sendFMutex);
    }
}


//文件接收
//选择要接收的文件
int recvFiles(char* fullpath)
{
    gsNode *gsList[CAPACITY], *cur;
    int tmp, index, count;
    char buf[FILENAME];
    struct stat dirAttr;
    pthread_t gFile;
    filenode *curFile;
    pthread_attr_t attr;

    while(1)
    {
        pthread_mutex_lock(&getFMutex);
        count = listGFiles(gsList, getFHead.next, sizeof(gsList)/sizeof(gsList[0])-1);
        pthread_mutex_unlock(&getFMutex);

        if (count == 0)
            return -1;
        else
            /*
            {
                printf("\n++++++++++++++++++++\n");
                printf("Files to get:\n");
                for(tmp=1; tmp<=count; tmp++)
                {
                    printf("%d. ", tmp);
                    curFile = (*(gsList+tmp-1))->fileList.next;
                    while (curFile!=NULL)
                    {
                        printf("%s ", curFile->fileName);
                        curFile = curFile->next;
                    }
                    printf("\n");
                }
                printf("++++++++++++++++++++\n");
            }

            index = selectOperation(1, count, 1,
                            "Which file(s) to get?(Ctrl+D to quit)\nNumber[1]:");
            */
            ///显式控制收取的文件为第一个
            index = 1;

        if (index<0)
            return -1;

        while(1)
        {
            /*
            printf("Where do you want to save?(Ctrl+D to quit)\nTarget dir[.]:");
            if (fgets(buf, sizeof(buf), stdin)==NULL)
                return -1;
            */
            ///再次搞起驱动程序，此为接收文件的路径
            memset(buf, '\0', strlen(buf));
            fuckTheString(buf, 0); //去除前后空白字符
            strcpy(buf, fullpath);
            ///接收文件的路径初始化ended
            if (buf[0]=='\0')
            {
                buf[0]='.';
                buf[1]='\0';
            }

            if ((stat(buf, &dirAttr)<0) ||
                    !S_ISDIR(dirAttr.st_mode) ||
                    (access(buf, W_OK)<0))
                printf("Invalid directory. Please input again.\n");
            else break;
        }

        cur = *(gsList+index-1);
        tmp = strlen(buf);
        pthread_mutex_lock(&getFMutex);
        if (cur->cancelled == 0)
        {
            cur->targetDir = (char*)malloc(tmp+1);
            strncpy(cur->targetDir, buf, tmp+1);
            cur->transferring = 1;
        }
        pthread_mutex_unlock(&getFMutex);
        ///设置独立线程用来接收数据
        pthread_attr_init(&attr);
        pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
        pthread_create(&gFile, &attr, &getData, cur);
    }
    return 0;
}

//当需要接收数据时，自动创建该文件接收线程
void* getData(void* option)
{
    gsNode *gList = (gsNode *)option;
    filenode *head=&gList->fileList, *cur;
    int fileType, tmp;
    command com;

    printf("\nStart getting files.\n");
    while((cur=head->next) != NULL)
    {
        fileType = cur->fileType & 0x03;
        switch (fileType)
        {
        case 1:
            tmp=getFile(cur, gList);
            break;
        case 2:
            tmp=getDir(cur, gList); //其实可以换成上次压缩再发送的做法
            break;
        default:
            tmp = -1;
            printf("\nUnsupported file type.\n%s fails to be transfered.\n", cur->fileName);
            break;
        }

        if (tmp<0)
        {
            initCommand(&com, IPMSG_RELEASEFILES);
            snprintf(com.additional, sizeof(com.additional), "%d", gList->packetNo);
            memcpy(&com.peer, &gList->peer, sizeof(com.peer));
            sendMsg(&com);
            pthread_mutex_lock(&getFMutex);
            gList->cancelled = 1;
            gList->transferring = 0;
            pthread_mutex_unlock(&getFMutex);
            printf("\n%s fails to be transfered.\n", cur->fileName);
            return NULL;
        }

        printf("\n%s is transferred.\n", cur->fileName);
        head->next = cur->next;
        free(cur);
    }

    pthread_mutex_lock(&getFMutex);
    gList->cancelled = 1; //其实是已经完成
    gList->transferring = 0;
    pthread_mutex_unlock(&getFMutex);

    printf("Finished Getting File(s)\n");

    //设置一个记号文件，表示文件收好了
    if(NULL == fopen("getfile.finished", "a+"))
    {
        FILE* fd;
        fd = fopen("getfile.finished", "a+");
        sleep(1);
        fclose(fd);
    }
    //搞完了休息一下，退出
    sleep(3);
    logout();
    destroyer();
    exit(0);
}

//接收文件
int getFile(void* option, gsNode *gList)
{
    int sockfd, readBytes;
    char buf[COMLEN];
    char recvs[RECFRG];
    filenode *head = (filenode*)option;
    command com;
    struct sockaddr_in peer;
    long fileSize, offset=0;
    FILE* recvFile;

    initCommand(&com, IPMSG_GETFILEDATA);  //Regular file
    snprintf(com.additional, MSGLEN, "%x:%x:%x", gList->packetNo, head->fileNo, offset);

    sockfd = socket(AF_INET, SOCK_STREAM, 0);

    bzero(&peer, sizeof(peer));
    peer.sin_family = AF_INET;
    peer.sin_port = htons(IPMSG_DEFAULT_PORT);
    memcpy(&peer.sin_addr, &gList->peer.sin_addr, sizeof(peer.sin_addr));

    if (connect(sockfd, (struct sockaddr*)&peer, sizeof(peer))<0)
    {
        printf("Failed to Connect.\n");
        return -1;
    }

    initMsg(buf, &com, sizeof(buf));

    if (writen(sockfd, buf, strlen(buf)+1)<0)
        return -1;

    sscanf(head->fileSize, "%x", &fileSize);

    if ((recvFile = fopen(head->fileName, "w+")) == NULL)
    {
        printf("Failed to create file.\n");
        return -1;
    }

    while (fileSize>0)
    {
        readBytes = fileSize < RECFRG ? fileSize:RECFRG;
        if ((readBytes = readn(sockfd, recvs, readBytes))<0) //RECFRG
        {
            printf("Data transfer error.\n");
            return -1;
        }

        fwrite(recvs, 1, readBytes, recvFile);
        fileSize -= readBytes;
    }
    fclose(recvFile);

    close(sockfd);
    return 0;
}

//接收文件夹时分析，头部
int parseHeader(filenode *pfn, char * recvs)
{
    char *strhead, *strtmp, gMsg[FILENAME];
    int i=0;

    strhead = recvs;
    while (i<3) //分析前3段
    {
        strtmp = strchr(strhead, ':');
        if (strtmp == NULL)
            return -1;
        *strtmp = '\0';

        switch (i)
        {
        case 0:

            strncpy(gMsg, strhead, sizeof(gMsg));
            delMaohao(gMsg, sizeof(gMsg));

            if (utf8)
                g2u(gMsg, sizeof(gMsg),
                    pfn->fileName, sizeof(pfn->fileName));
            else strncpy(pfn->fileName, gMsg, sizeof(pfn->fileName));
            break;

        case 1:
            //sscanf(strhead, "%x", &pfn->fileSize);
            strncpy(pfn->fileSize, strhead, sizeof(pfn->fileSize));
            break;
        case 2:
            sscanf(strhead, "%x", &pfn->fileType);
            break;

        default:
            break;
        }
        strhead = strtmp+1;
        i++;
    }

    strncpy(pfn->otherAttrs, strhead, sizeof(pfn->otherAttrs));

    return 0;
}

//接收文件夹
int getDir(void *option, gsNode *gList)
{
    int sockfd, readBytes, headerSize, fileType, dirDepth=0, tmp;
    char buf[COMLEN], recvs[RECFRG], hSize[HSIZE], fullPath[2*FILENAME];
    filenode *head = (filenode*)option, *cur, fn;
    command com;
    struct sockaddr_in peer;
    long  offset=0, fileSize;
    FILE* recvFile;

    strncpy(fullPath, gList->targetDir, sizeof(fullPath));

    initCommand(&com, IPMSG_GETDIRFILES); //Direcotry

    snprintf(com.additional, MSGLEN, "%x:%x:%x", gList->packetNo, head->fileNo, offset);

    sockfd = socket(AF_INET, SOCK_STREAM, 0);

    bzero(&peer, sizeof(peer));
    peer.sin_family = AF_INET;
    peer.sin_port = htons(IPMSG_DEFAULT_PORT);
    memcpy(&peer.sin_addr, &gList->peer.sin_addr, sizeof(peer.sin_addr));

    if (connect(sockfd, (struct sockaddr*)&peer, sizeof(peer))<0)
    {
        printf("File connect error.\n");
        return -1;
    }

    initMsg(buf, &com, sizeof(buf));

    if (writen(sockfd, buf, strlen(buf)+1)<0)
        return -1;

    do
    {
        tmp = strlen(fullPath);
        if (tmp+1>=sizeof(fullPath))
        {
            printf("getDir: target directory is too lang.\n");
            return -1;
        }
        if (fullPath[tmp-1]!='/')
        {
            fullPath[tmp] = '/';
            fullPath[tmp+1] = '\0';
        }

        readBytes = readMaohao(sockfd, hSize, HSIZE, ':');
        sscanf(hSize, "%x", &headerSize);
        if (headerSize<=0 || headerSize<=readBytes || headerSize>RECFRG) //保护、防止溢出
            return -1;
        readn(sockfd, recvs, headerSize-readBytes);
        recvs[headerSize-readBytes]='\0';

        if (parseHeader(&fn, recvs)<0)
        {
            printf("getDir: Parse protocol failed.\n");
            return -1;
        }

        switch (fn.fileType & 0x03)
        {
        case IPMSG_FILE_REGULAR:

            strncat(fullPath, fn.fileName, sizeof(fullPath)-tmp-1);
            if ((recvFile = fopen(fullPath, "w+")) == NULL)
            {
                printf("Open error.\n");
                return -1;
            }

            sscanf(fn.fileSize, "%x", &fileSize);
            while (fileSize>0)
            {
                readBytes = fileSize < RECFRG ? fileSize:RECFRG;
                if ((readBytes = readn(sockfd, recvs, readBytes))<0) //RECFRG
                {
                    printf("File read error.\n");
                    return -1;
                }

                fwrite(recvs, 1, readBytes, recvFile);
                fileSize -= readBytes;
            }
            fclose(recvFile);
            getParentPath(fullPath, sizeof(fullPath));
            break;

        case IPMSG_FILE_DIR:
            strncat(fullPath, fn.fileName, sizeof(fullPath)-tmp-1);
            tmp = strlen(fullPath);
            if (tmp+1>=sizeof(fullPath))
            {
                printf("getDir: target directory is too long.\n"
                       "Filename(%s) has been truncated.\n", fn.fileName);
                fullPath[tmp-1] = '/';
            }
            else
            {
                fullPath[tmp] = '/';
                fullPath[tmp+1] = '\0';
            }

            if (mkdir(fullPath, S_IRUSR|S_IWUSR|S_IXUSR))
            {
                printf("getDir: mkdir failed.\n");
                return -1;
            }

            dirDepth++;
            break;

        case IPMSG_FILE_RETPARENT:
            getParentPath(fullPath, sizeof(fullPath));
            dirDepth--;
            break;

        default:
            printf("Unsupported file type.\n");
            break;
        }

    }
    while(dirDepth>0);

    shutdown(sockfd, SHUT_RDWR);
    return 0;
}

//列举待接收文件
int listGFiles(gsNode **list, gsNode *gs, int size)
{
    filenode *curFile;
    int tmp=0;

    while ((gs!=NULL)&&(tmp<size))
    {
        if ((gs->transferring==0)&&(gs->cancelled==0))
        {
            *list++ = gs;
            tmp++;
        }
        gs = gs->next;
    }
    return tmp;
}

//登录
int login()
{
    command com;

    initCommand(&com, IPMSG_BR_ENTRY);

    com.peer.sin_family = AF_INET;
    com.peer.sin_port = htons(IPMSG_DEFAULT_PORT);

    if (inet_pton(AF_INET, allHosts, &com.peer.sin_addr)<0)
        printf("login: Ip error.\n");

    strncpy(com.additional, pwd->pw_name, MSGLEN);

    sendMsg(&com);
}

//退出
int logout()
{
    command com;

    initCommand(&com, IPMSG_BR_EXIT);

    com.peer.sin_family = AF_INET;
    com.peer.sin_port = htons(IPMSG_DEFAULT_PORT);

    if (inet_pton(AF_INET, allHosts, &com.peer.sin_addr)<0)
        printf("logout: error\n");

    strncpy(com.additional, pwd->pw_name, MSGLEN);

    sendMsg(&com);
    printf("LinPop File Support module is exiting...\n\n");
}
